﻿using MvvmCross.Core.ViewModels;
using Sidvall.Data;
using Sidvall.IdentityManager.Data;
using Sidvall.IdentityManager.UI.Entities;
using Sidvall.IdentityManager.UI.Messaging;
using Sidvall.Serialization;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Input;

namespace Sidvall.IdentityManager.UI.ViewModels
{
    public partial class UserConfigurationViewModel
    {
        #region Public Members

        #region ActiveServerItem

        public ServerItem ActiveServerItem
        {
            get
            {
                if (this.ActiveEntity == null)
                    return null;
                if (this.ActiveServerItemId == null)
                    return null;
                return (from o in this.ActiveEntity.ServerItems
                        where o.ServerItemId == this.ActiveServerItemId
                        select o).FirstOrDefault();
            }
        }

        #endregion
        #region ActiveServerItemId

        private int? _ActiveServerItemId;
        public int? ActiveServerItemId
        {
            get
            {
                return _ActiveServerItemId;
            }
            set
            {
                if (_ActiveServerItemId == value)
                    return;
                _ActiveServerItemId = value;
                RaiseAllPropertiesChanged();
            }
        }

        #endregion
        #region ActiveClassname

        private string _ActiveClassname;
        public string ActiveClassname
        {
            get
            {
                return _ActiveClassname;
            }
            set
            {
                _ActiveClassname = value;
                RaisePropertyChanged("ActiveClassname");
            }
        }

        #endregion
        #region ActivePlugin

        private Plugin _ActivePlugin;
        public Plugin ActivePlugin
        {
            get
            {
                return _ActivePlugin;
            }
            set
            {
                _ActivePlugin = value;
                RaisePropertyChanged("ActivePlugin");
                RaisePropertyChanged("ActivePluginName");
                RaisePropertyChanged("CanEditPlugin");
            }
        }

        #endregion
        #region ActivePluginName

        public string ActivePluginName
        {
            get
            {
                if (this.ActivePlugin == null)
                    return string.Empty;
                return this.ActivePlugin.TypeName;
            }
        }

        #endregion
        #region ClassNames

        private System.Collections.ObjectModel.ObservableCollection<string> _ClassNames;
        public System.Collections.ObjectModel.ObservableCollection<string> ClassNames
        {
            get
            {
                if (_ClassNames == null)
                    this.ClassNames = new System.Collections.ObjectModel.ObservableCollection<string>();
                return _ClassNames;
            }
            private set
            {
                _ClassNames = value;
                RaisePropertyChanged("ClassNames");
            }
        }

        #endregion
        #region ClassDialogIsVisible

        private bool _ClassDialogIsVisible;
        public bool ClassDialogIsVisible
        {
            get
            {
                return _ClassDialogIsVisible;
            }
            set
            {
                _ClassDialogIsVisible = value;
                RaisePropertyChanged("ClassDialogIsVisible");
            }
        }

        #endregion
        #region ConnectionTypes

        private Sidvall.Data.KeyValueCollection<string, Data.ConnectionType> _ConnectionTypes;
        public Sidvall.Data.KeyValueCollection<string, Data.ConnectionType> ConnectionTypes
        {
            get
            {
                if (_ConnectionTypes == null)
                    this.ConnectionTypes = Sidvall.Data.KeyValueManager.FromEnum<Sidvall.IdentityManager.Data.ConnectionType>();
                return _ConnectionTypes;
            }
            set
            {
                _ConnectionTypes = value;
                RaisePropertyChanged("ConnectionTypes");
            }
        }

        #endregion
        #region ShowSqlServerServer

        public bool ShowSqlServerServer
        {
            get
            {
                if (this.ActiveServerItem == null)
                    return false;
                return this.ActiveServerItem.IsSqlServerServer;
            }
        }

        #endregion
        #region ShowRemoteServer

        public bool ShowRemoteServer
        {
            get
            {
                if (this.ActiveServerItem == null)
                    return false;
                return this.ActiveServerItem.IsRemoteServer;
            }
        }

        #endregion
        #region CanConnectToServer

        public bool CanConnectToServer
        {
            get
            {
                if (this.ActiveServerItemId == null)
                    return false;
                return true;
            }
        }

        #endregion
        #region CanDeleteServerItem

        public bool CanDeleteServerItem
        {
            get
            {
                if (this.ActiveServerItemId == null)
                    return false;
                if (this.ActiveServerItemId == Sidvall.IdentityManager.SystemContext.Current.ActiveServerItemId)
                    return false;
                return true;
            }
        }

        #endregion
        #region CanEditPlugin

        public bool CanEditPlugin
        {
            get
            {
                if (this.ActivePlugin == null)
                    return false;
                return true;
            }
        }

        #endregion
        #region PageTypes

        private Sidvall.Data.KeyValueCollection<string, Sidvall.IdentityManager.Data.PageType> _PageTypes;
        public Sidvall.Data.KeyValueCollection<string, Sidvall.IdentityManager.Data.PageType> PageTypes
        {
            get
            {
                if (_PageTypes == null)
                    this.PageTypes = Sidvall.Data.KeyValueManager.FromEnum<Sidvall.IdentityManager.Data.PageType>();
                return _PageTypes;
            }
            set
            {
                _PageTypes = value;
                RaisePropertyChanged("PageTypes");
            }
        }

        #endregion
        #region ServerPasswordIsVisible

        private bool _ServerPasswordIsVisible;
        public bool ServerPasswordIsVisible
        {
            get
            {
                return _ServerPasswordIsVisible;
            }
            set
            {
                _ServerPasswordIsVisible = value;
                RaisePropertyChanged("ServerPasswordIsVisible");
            }
        }

        #endregion
        #region TwoFactorProviders

        private Sidvall.Data.KeyValueCollection<string, Sidvall.Security.TwoFactorProvider> _TwoFactorProviders;
        public Sidvall.Data.KeyValueCollection<string, Sidvall.Security.TwoFactorProvider> TwoFactorProviders
        {
            get
            {
                if (_TwoFactorProviders == null)
                    this.TwoFactorProviders = Sidvall.Data.KeyValueManager.FromEnum<Sidvall.Security.TwoFactorProvider>();
                return _TwoFactorProviders;
            }
            set
            {
                _TwoFactorProviders = value;
                RaisePropertyChanged(nameof(TwoFactorProviders));
            }
        }

        #endregion

        // Commands
        #region AddServerItemCommand

        public ICommand AddServerItemCommand
        {
            get
            {
                return new MvxCommand(async () => await AddServerItemAsync());
            }
        }

        #endregion
        #region AddServerItemAsync

        public async Task AddServerItemAsync()
        {
            ServerItem item;
            Task task = null;

            using (var busyManager = new Sidvall.Data.BusyManager(this))
            {
                try
                {
                    item = new ServerItem()
                    {
                        ServerItemId = Sidvall.IdentityManager.SystemContextConfig.Current.EntityFacade.UserConfigurationManager.GetFreeServerItemId(this.ActiveEntity),
                        Name = "** Ny server **",
                        ConnectionTypeId = Data.ConnectionType.NetworkSqlServer,
                    };
                    this.ActiveEntity.ServerItems.Add(item);
                    await DoSaveAsync();
                    this.ActiveServerItemId = item.ServerItemId;
                }
                catch (System.Exception ex)
                {
                    task = Sidvall.UI.SystemContext.Current.DialogManager.ShowErrorMessageAsync("Unexpected Error", ex);
                }
                if (task != null)
                    await task;
            }
        }

        #endregion
        #region DeleteServerItemCommand

        public ICommand DeleteServerItemCommand
        {
            get
            {
                return new MvxCommand(async () => await DeleteServerItemAsync());
            }
        }

        #endregion
        #region DeleteServerItemAsync

        public async Task DeleteServerItemAsync()
        {
            Task task = null;
            if (!this.CanDeleteServerItem)
                return;
            using (var busyManager = new Sidvall.Data.BusyManager(this))
            {
                try
                {
                    this.ActiveEntity.ServerItems.Remove(this.ActiveServerItem);
                    await DoSaveAsync();
                    this.ActiveServerItemId = Sidvall.IdentityManager.SystemContext.Current.ActiveServerItemId;
                }
                catch (System.Exception ex)
                {
                    task = Sidvall.UI.SystemContext.Current.DialogManager.ShowErrorMessageAsync("Unexpected Error", ex);
                }
                if (task != null)
                    await task;
            }
        }

        #endregion
        #region ExportSettingsCommand

        public ICommand ExportSettingsCommand
        {
            get
            {
                return new MvxCommand(async () => await ExportSettingsAsync());
            }
        }

        #endregion
        #region ExportSettingsAsync

        public async Task ExportSettingsAsync()
        {
            Sidvall.IdentityManager.Net.SimpleServices.V1.UserConfiguration userConfiguration;
            string fileName, content;
            Task task = null;

            try
            {
                fileName = await Sidvall.UI.SystemContext.Current.DialogManager.SaveFileAsync(null);
                if (string.IsNullOrWhiteSpace(fileName))
                    return;
                userConfiguration = new Net.SimpleServices.V1.UserConfiguration();
                userConfiguration.Import(this.ActiveEntity, Sidvall.Data.CopySettings.FullCopyAll(Sidvall.Data.DataRowContextOption.None, null));
                content = Sidvall.Serialization.SerializerManager.Current.SerializeObject(userConfiguration);
                await Sidvall.SystemContext.Current.SystemStorageManager.WriteAllTextAsync(fileName, content);
            }
            catch (System.Exception ex)
            {
                task = Sidvall.UI.SystemContext.Current.DialogManager.ShowErrorMessageAsync("Unexpected Error", ex);
            }
            if (task != null)
                await task;
        }

        #endregion
        #region ImportSettingsCommand

        public ICommand ImportSettingsCommand
        {
            get
            {
                return new MvxCommand(async () => await ImportSettingsAsync());
            }
        }

        #endregion
        #region ImportSettingsAsync

        public async Task ImportSettingsAsync()
        {
            Task task = null;
            string fileName;
            string content;

            try
            {
                fileName = await Sidvall.UI.SystemContext.Current.DialogManager.OpenFileAsync("Text Files (*.txt)|*.txt|All Files (*.*)|*.*", "*.txt");
                if (string.IsNullOrWhiteSpace(fileName))
                    return;
                content = await Sidvall.SystemContext.Current.SystemStorageManager.ReadAllTextAsync(fileName);
                var item = Sidvall.IdentityManager.UI.SystemContext.Current.LoadUserConfiguration(content);
                this.ActiveEntity = item.Copy(Sidvall.Data.CopySettings.FullCopyAll(Sidvall.Data.DataRowContextOption.State, null));
                this.ActiveEntity = await Sidvall.IdentityManager.UI.SystemContext.Current.SaveUserConfigurationAsync(this.ActiveEntity, true);
            }
            catch (System.Exception ex)
            {
                task = Sidvall.UI.SystemContext.Current.DialogManager.ShowErrorMessageAsync("Unexpected Error", ex);
            }
            if (task != null)
                await task;
        }

        #endregion
        #region ConnectToServerCommand

        public ICommand ConnectToServerCommand
        {
            get
            {
                return new MvxCommand(async () => await ConnectToServerAsync());
            }
        }

        #endregion
        #region ConnectToServerAsync

        public async Task ConnectToServerAsync()
        {
            Task task = null;
            if (!this.CanConnectToServer)
                return;
            using (var busyManager = new Sidvall.Data.BusyManager(this))
            {
                try
                {
                    Sidvall.IdentityManager.SystemContext.Current.ActiveServerItemId = this.ActiveServerItemId;
                    this.ActiveEntity.DefaultServerItemId = this.ActiveServerItemId.Value;
                    await DoSaveAsync();
                    await Sidvall.IdentityManager.UI.SystemContext.Current.LoadServerInfoAsync(true, true);
                    await Sidvall.IdentityManager.UI.SystemContext.Current.InitializePluginsAsync();
                    await DoLoadAsync();
                    this.ActiveServerItemId = Sidvall.IdentityManager.SystemContext.Current.ActiveServerItemId;
                }
                catch (System.Exception ex)
                {
                    task = Sidvall.UI.SystemContext.Current.DialogManager.ShowErrorMessageAsync("Unexpected Error", ex);
                }
                if (task != null)
                    await task;
            }
        }

        #endregion
        #region ShowNewsCommand

        public ICommand ShowNewsCommand
        {
            get
            {
                return new MvxCommand(ShowNews);
            }
        }

        #endregion
        #region ShowNews

        public void ShowNews()
        {
            ShowViewModel<NewsViewModel>((object)null, true, "CenterContent");
        }

        #endregion
        #region ViewServerPasswordCommand

        public ICommand ViewServerPasswordCommand
        {
            get
            {
                return new MvxCommand<string>(o => ViewServerPassword(o));
            }
        }

        #endregion
        #region ViewServerPassword

        public void ViewServerPassword(string showPassword)
        {
            this.ServerPasswordIsVisible = showPassword.ToBoolean(false);
        }

        #endregion
        #region OpenFileCommand

        public ICommand OpenFileCommand
        {
            get
            {
                return new MvxCommand<string>(async (o) => await OpenFileAsync(o));
            }
        }

        #endregion
        #region OpenFileAsync

        public async Task OpenFileAsync(string id)
        {
            Task task = null;
            string[] classNames;
            string file, baseTypeName;

            try
            {
                file = await Sidvall.UI.SystemContext.Current.DialogManager.OpenFileAsync("Libraries (*.dll)|*.dll|All Files (*.*)|*.*", ".dll");
                if (!string.IsNullOrWhiteSpace(file))
                {
                    _ActiveAssembly = await Sidvall.SystemContext.Current.CodeManager.GetAssemblyAsync(file);
                    switch (id)
                    {
                        case "1":
                            _ClassNameType = ClassNameType.Plugin1;
                            baseTypeName = "Sidvall.IdentityManager.UI.IPlugin";
                            classNames = Sidvall.SystemContext.Current.CodeManager.GetAssemblyTypeNames(_ActiveAssembly, true, false, true, typeof(IPlugin).GetTypeInfo());
                            break;
                        default:
                            classNames = null;
                            baseTypeName = null;
                            break;
                    }
                    if ((classNames == null) || (!classNames.Any()))
                    {
                        await Sidvall.UI.SystemContext.Current.DialogManager.ShowMessageAsync("No classes found",
                            "Could not find any classes in '" + file + "' that inherits from " + baseTypeName, Sidvall.UI.Dialogs.MessageType.Information);
                        return;
                    }
                    this.ClassNames.Clear();
                    foreach (var className in classNames)
                        this.ClassNames.Add(className);
                    this.ClassDialogIsVisible = true;
                }
            }
            catch (System.Exception ex)
            {
                task = Sidvall.UI.SystemContext.Current.DialogManager.ShowErrorMessageAsync("Unexpected Error", ex);
            }
            if (task != null)
                await task;
        }

        #endregion
        #region SelectFileNameCommand

        public ICommand SelectFileNameCommand
        {
            get
            {
                return new MvxCommand(async () => await SelectFileNameAsync());
            }
        }

        #endregion
        #region SelectFileNameAsync

        public async Task SelectFileNameAsync()
        {
            Task task = null;
            string typeName;

            if (string.IsNullOrWhiteSpace(this.ActiveClassname))
                return;
            try
            {
                using (var busyManager = new BusyManager(this))
                {
                    typeName = this.ActiveClassname + ", " + _ActiveAssembly.GetName().Name;
                    switch (_ClassNameType)
                    {
                        case ClassNameType.Plugin1:
                            this.ActivePlugin.TypeName = typeName;
                            break;
                        default:
                            break;
                    }
                }
            }
            catch (System.Exception ex)
            {
                task = Sidvall.UI.SystemContext.Current.DialogManager.ShowErrorMessageAsync("Unexpected Error", ex);
            }
            finally
            {
                CloseFileNameDialog();
            }
            if (task != null)
                await task;
        }

        #endregion
        #region CloseFileNameDialogCommand

        public ICommand CloseFileNameDialogCommand
        {
            get
            {
                return new MvxCommand(CloseFileNameDialog);
            }
        }

        #endregion
        #region CloseFileNameDialog

        public void CloseFileNameDialog()
        {
            this.ClassDialogIsVisible = false;
        }

        #endregion
        #region OpenFolderCommand

        public ICommand OpenFolderCommand
        {
            get
            {
                return new MvxCommand(async () => await OpenFolderAsync());
            }
        }

        #endregion
        #region OpenFolderAsync

        public async Task OpenFolderAsync()
        {
            Task task = null;
            string path;

            try
            {
                path = await Sidvall.UI.SystemContext.Current.DialogManager.OpenFolderAsync(Sidvall.UI.Dialogs.SpecialFolder.Desktop, this.ActiveEntity.PluginPath, true);
                if (!string.IsNullOrWhiteSpace(path))
                {
                    this.ActiveEntity.PluginPath = path;
                }
            }
            catch (System.Exception ex)
            {
                task = Sidvall.UI.SystemContext.Current.DialogManager.ShowErrorMessageAsync("Unexpected Error", ex);
            }
            if (task != null)
                await task;
        }

        #endregion
        #region AddPluginCommand

        public ICommand AddPluginCommand
        {
            get
            {
                return new MvxCommand(async () => await AddPluginAsync());
            }
        }

        #endregion
        #region AddPluginAsync

        public async Task AddPluginAsync()
        {
            Task task = null;
            Plugin item;

            using (var busyManager = new Sidvall.Data.BusyManager(this))
            {
                try
                {
                    item = await this.ActiveServerItem.AddPluginAsync(null);
                    this.ActivePlugin = item;
                }
                catch (System.Exception ex)
                {
                    task = Sidvall.UI.SystemContext.Current.DialogManager.ShowErrorMessageAsync("Unexpected Error", ex);
                }
                if (task != null)
                    await task;
            }
        }

        #endregion
        #region DeletePluginCommand

        public ICommand DeletePluginCommand
        {
            get
            {
                return new MvxCommand<Plugin>(async (o) => await DeletePluginAsync(o));
            }
        }

        #endregion
        #region DeletePluginAsync

        public async Task DeletePluginAsync(Plugin item)
        {
            Task task = null;
            using (var busyManager = new Sidvall.Data.BusyManager(this))
            {
                try
                {
                    this.ActiveServerItem.Plugins.Remove(item);
                }
                catch (System.Exception ex)
                {
                    task = Sidvall.UI.SystemContext.Current.DialogManager.ShowErrorMessageAsync("Unexpected Error", ex);
                }
                if (task != null)
                    await task;
            }
        }

        #endregion
        #region ImportPluginsCommand

        public ICommand ImportPluginsCommand
        {
            get
            {
                return new MvxCommand(async () => await ImportPluginsAsync());
            }
        }

        #endregion
        #region ImportPluginsAsync

        public async Task ImportPluginsAsync()
        {
            Task task = null;
            Plugin item;

            try
            {
                var classNames = await CodeManager.GetClassNamesAsync(this.ActiveEntity.PluginPath);
                foreach (var className in classNames)
                {
                    item = (from o in this.ActiveServerItem.Plugins
                            where o.TypeName.Equals(className, System.StringComparison.OrdinalIgnoreCase)
                            select o).FirstOrDefault();
                    if (item == null)
                    {
                        item = await this.ActiveServerItem.AddPluginAsync(className);
                        if (this.ActivePlugin == null)
                            this.ActivePlugin = item;
                    }
                }
            }
            catch (System.Exception ex)
            {
                task = Sidvall.UI.SystemContext.Current.DialogManager.ShowErrorMessageAsync("Unexpected Error", ex);
            }
            if (task != null)
                await task;
        }

        #endregion

        #region OnActiveEntityChanged

        protected override void OnActiveEntityChanged(Sidvall.Business.BusinessObjectChangeMode changeMode, Sidvall.Business.ChildChangedEventArgs childArgs, System.ComponentModel.PropertyChangedEventArgs propertyArgs)
        {
            RaiseAllPropertiesChanged();
            if (changeMode == Sidvall.Business.BusinessObjectChangeMode.NewBusinessObject)
            {
                if (this.ActiveServerItemId != _CurrentServerItemId)
                    this.ActiveServerItemId = _CurrentServerItemId;
            }
        }

        #endregion
        #region LoadItemAsync

        protected override Task<UserConfiguration> LoadItemAsync()
        {
            var item = Sidvall.IdentityManager.UI.SystemContext.Current.UserConfiguration.Copy(Sidvall.Data.CopySettings.FullCopyAll(Sidvall.Data.DataRowContextOption.State, null));
            return Task.FromResult(item);
        }

        #endregion
        #region SaveItemAsync

        protected override async Task<Entities.UserConfiguration> SaveItemAsync(UserConfiguration item)
        {
            _CurrentServerItemId = this.ActiveServerItemId;
            item = await Sidvall.IdentityManager.UI.SystemContext.Current.SaveUserConfigurationAsync(item, false);

            return item.Copy(Sidvall.Data.CopySettings.FullCopyAll(Sidvall.Data.DataRowContextOption.State, null));
        }

        #endregion

        #region Init

        public void Init(UserConfigurationParameter item)
        {
            this.Parameter = item;
        }

        #endregion
        #region StartAsync

        protected override async Task StartAsync()
        {
            _CurrentServerItemId = Sidvall.IdentityManager.SystemContext.Current.ActiveServerItemId;
            await DoLoadAsync();
        }

        #endregion

        #endregion
        #region Private Members

        private UserConfigurationParameter Parameter { get; set; }
        private Assembly _ActiveAssembly;
        private ClassNameType _ClassNameType;
        private int? _CurrentServerItemId;

        #endregion
    }
}
